/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Global Trust Authority is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

use std::{env, sync};
use crate::config::config::{KEY_MANAGER_ACTIX_WEB_WORKERS, KEY_MANAGER_ADDR, KEY_MANAGER_CERT_PATH, KEY_MANAGER_KEY_PATH, KEY_MANAGER_LOG_LEVEL, KEY_MANAGER_LOG_PATH, KEY_MANAGER_PORT, KEY_MANAGER_ROOT_TOKEN, KEY_MANAGER_SECRET_ADDR, ROOT_CA_CERT_PATH};
use crate::utils::errors::AppError;

/// Loads environment variables from a `.env` file located in the same directory as the executable.
///
/// # Returns
///
/// Returns `Ok(())` on success, or a `Box<dyn std::error::Error>` if an error occurs
/// (e.g., failing to get the current executable path, its parent directory, or loading the `.env` file).
/// 
/// # Errors
/// 
/// * `AppError::EnvConfigError` - If the environment variable is not found or cannot be parsed.
pub fn load_env() -> Result<(), Box<dyn std::error::Error>> {
    let exe_path = env::current_exe()?;
    let bin_dir = if let Some(dir) = exe_path.parent() {
        dir
    } else {
        return Err("failed to get parent directory".into());
    };
    let env_path = bin_dir.join(".env");
    dotenv::from_path(env_path)?;
    Ok(())
}

#[derive(Debug)]
pub struct Environment {
    pub addr : String,
    pub port : u16,
    pub cert: String,
    pub private_key: String,
    pub root_ca_cert: String,
    pub log_level : String,
    pub log_path : String,
    pub root_token: String,
    pub bao_addr: String,
    pub actix_web_workers: u16,
}

/// Holds the global environment configuration.
///
/// This static variable is used to store the singleton instance of the `Environment` struct,
/// which holds the configuration values loaded from environment variables.
pub static ENVIRONMENT_CONFIG: sync::OnceLock<Environment> = sync::OnceLock::new();

impl Environment {

    /// Creates a default, empty instance of `Environment`.
    ///
    /// All fields are initialized to default values (0 for u16, empty strings for String).
    pub fn default() -> Self {
        Self {
            addr: String::new(),
            port : 0,
            cert: String::new(),
            private_key: String::new(),
            root_ca_cert: String::new(),
            log_level : String::new(),
            log_path : String::new(),
            root_token: String::new(),
            bao_addr: String::new(),
            actix_web_workers: 0,
        }
    }

    /// Checks if all required environment variables are set and can be parsed.
    ///
    /// This function calls individual `get_*` functions to validate the presence
    /// and format of each expected environment variable.
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` if all required environment variables are found and valid,
    /// or an `AppError::EnvConfigError` if any required variable is missing or invalid.
    pub fn check() -> Result<(), AppError> {
        get_key_manager_addr()?;
        get_port()?;
        get_cert()?;
        get_key()?;
        get_root_ca_cert()?;
        get_log_level()?;
        get_log_path()?;
        get_root_token()?;
        get_bao_addr()?;
        get_actix_web_workers()?;
        Ok(())
    }

    pub fn global() -> &'static Environment {
        ENVIRONMENT_CONFIG.get_or_init(|| {
            let mut environment = Environment::default();
            environment.addr = get_key_manager_addr().expect("failed to get key_manager_addr string");
            environment.port = get_port().expect("failed to get port number");
            environment.cert = get_cert().expect("failed to get cert string");
            environment.private_key = get_key().expect("failed to get private key string");
            environment.root_ca_cert = get_root_ca_cert().expect("failed to get root_ca_cert string");
            environment.log_level = get_log_level().expect("failed to get log level");
            environment.log_path = get_log_path().expect("failed to get log path");
            environment.root_token = get_root_token().expect("failed to get root_token string");
            environment.bao_addr = get_bao_addr().expect("failed to get bao_addr string");
            environment.actix_web_workers = get_actix_web_workers().expect("failed to get actix_web_workers number");
            environment
        })
    }
}

/// Retrieves the port number from the environment variable `KEY_MANAGER_PORT`.
///
/// # Returns
///
/// Returns the parsed port number as a `u16` if successful,
/// or an `AppError::EnvConfigError` if the variable is not set or cannot be parsed.
pub fn get_port() -> Result<u16, AppError> {
    let port_str = env::var(KEY_MANAGER_PORT).map_err(|_| AppError::EnvConfigError(String::from(KEY_MANAGER_PORT)))?;
    let port = port_str.parse::<u16>().map_err(|_| AppError::EnvConfigError(String::from(KEY_MANAGER_PORT)))?;
    Ok(port)
}

/// Retrieves the address from the environment variable `KEY_MANAGER_ADDR`.
///
/// # Returns
///
/// Returns the parsed address as a `String` if successful,
/// or an `AppError::EnvConfigError` if the variable is not set or cannot be parsed.
pub fn get_key_manager_addr() -> Result<String, AppError> {
    let addr = env::var(KEY_MANAGER_ADDR).map_err(|_| AppError::EnvConfigError(String::from(KEY_MANAGER_ADDR)))?;
    Ok(addr)
}

/// Retrieves the path to the certificate file from the environment variable `KEY_MANAGER_CERT_PATH`.
///
/// # Returns
///
/// Returns the parsed certificate path as a `String` if successful,
/// or an `AppError::EnvConfigError` if the variable is not set or cannot be parsed.
pub fn get_cert() -> Result<String, AppError> {
    let cert = env::var(KEY_MANAGER_CERT_PATH).map_err(|_| AppError::EnvConfigError(String::from(KEY_MANAGER_CERT_PATH)))?;
    Ok(cert)
}

/// Retrieves the path to the private key file from the environment variable `KEY_MANAGER_KEY_PATH`.
///
/// # Returns
///
/// Returns the parsed private key path as a `String` if successful,
/// or an `AppError::EnvConfigError` if the variable is not set or cannot be parsed.
pub fn get_key() -> Result<String, AppError> {
    let key = env::var(KEY_MANAGER_KEY_PATH).map_err(|_| AppError::EnvConfigError(String::from(KEY_MANAGER_KEY_PATH)))?;
    Ok(key)
}

/// Retrieves the log level from the environment variable `KEY_MANAGER_LOG_LEVEL`.
///
/// # Returns
///
/// Returns the parsed log level as a `String` if successful,
/// or an `AppError::EnvConfigError` if the variable is not set or cannot be parsed.
pub fn get_log_level() -> Result<String, AppError> {
    let log_level = env::var(KEY_MANAGER_LOG_LEVEL).map_err(|_| AppError::EnvConfigError(String::from(KEY_MANAGER_LOG_LEVEL)))?;
    Ok(log_level)
}


/// Retrieves the log path from the environment variable `KEY_MANAGER_LOG_PATH`.
///
/// # Returns
///
/// Returns the parsed log path as a `String` if successful,
/// or an `AppError::EnvConfigError` if the variable is not set or cannot be parsed.
pub fn get_log_path() -> Result<String, AppError> {
    let log_path = env::var(KEY_MANAGER_LOG_PATH).map_err(|_| AppError::EnvConfigError(String::from(KEY_MANAGER_LOG_PATH)))?;
    Ok(log_path)
}

/// Retrieves the root token from the environment variable `KEY_MANAGER_ROOT_TOKEN`.
///
/// # Returns
///
/// Returns the parsed root token as a `String` if successful,
/// or an `AppError::EnvConfigError` if the variable is not set or cannot be parsed.
pub fn get_root_token() -> Result<String, AppError> {
    let root_token = env::var(KEY_MANAGER_ROOT_TOKEN).map_err(|_| AppError::EnvConfigError(String::from(KEY_MANAGER_ROOT_TOKEN)))?;
    Ok(root_token)
}

/// Retrieves the secret address from the environment variable `KEY_MANAGER_SECRET_ADDR`.
///
/// # Returns
///
/// Returns the parsed secret address as a `String` if successful,
/// or an `AppError::EnvConfigError` if the variable is not set or cannot be parsed.
pub fn get_bao_addr() -> Result<String, AppError> {
    let addr = env::var(KEY_MANAGER_SECRET_ADDR).map_err(|_| AppError::EnvConfigError(String::from(KEY_MANAGER_SECRET_ADDR)))?;
    Ok(addr)
}

/// Retrieves the path to the root CA certificate file from the environment variable `ROOT_CA_CERT_PATH`.
///
/// # Returns
///
/// Returns the parsed root CA certificate path as a `String` if successful,
/// or an `AppError::EnvConfigError` if the variable is not set or cannot be parsed.
pub fn get_root_ca_cert() -> Result<String, AppError> {
    let ca_cert = env::var(ROOT_CA_CERT_PATH).map_err(|_| AppError::EnvConfigError(String::from(ROOT_CA_CERT_PATH)))?;
    Ok(ca_cert)
}

/// Retrieves the number of Actix Web workers from the environment variable `KEY_MANAGER_ACTIX_WEB_WORKERS`.
///
/// # Returns
///
/// Returns the parsed number of Actix Web workers as a `u16` if successful,
/// or an `AppError::EnvConfigError` if the variable is not set or cannot be parsed.
pub fn get_actix_web_workers() -> Result<u16, AppError> {
    let workers_str = env::var(KEY_MANAGER_ACTIX_WEB_WORKERS).map_err(|_| AppError::EnvConfigError(String::from(KEY_MANAGER_ACTIX_WEB_WORKERS)))?;
    let workers = workers_str.parse::<u16>().map_err(|_| AppError::EnvConfigError(String::from(KEY_MANAGER_ACTIX_WEB_WORKERS)))?;
    Ok(workers)
}

#[cfg(test)]
mod tests {
    use std::path::Path;
    use super::Environment;

    #[test]
    fn test_env_check() {
        let test_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/testdata");
        let config_path = test_dir.join(".env");
        let _ = dotenv::from_path(config_path);
        match Environment::check() {
            Ok(_) => {
                assert!(true);
            },
            Err(_err) => {
                assert!(false);
            }
        }
    }
}